#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "transformada.h"
#include "tipoAudio.h"

//Recebe um vetor de dados a aplica a transformada MDCT
void comprimir_transformada(char *input, char *output){

    FILE *finput;
    int tam_arquivo;
    int num_amostras;
    unsigned short int *amostras_entrada;
    double *amostras_saida;

    finput = fopen(input, "rb");

    //Calculo do tamanho do arquivo de audio
	fseek(finput, sizeof(wav_header), SEEK_SET);
	tam_arquivo = ftell(finput);
	fseek(finput, 0, SEEK_END);
	tam_arquivo = ftell(finput) - tam_arquivo;
	fseek(finput, sizeof(wav_header), SEEK_SET);

	//quantidade de short ints no arquivo (sem o header)
	num_amostras = tam_arquivo/16;

	//amostras entrada eh o vetor com os dados do arquivo de audio da entrada
    amostras_entrada = (unsigned short int *) malloc(sizeof(unsigned short int) * num_amostras);

    fread(amostras_entrada, sizeof(unsigned short int), num_amostras, finput);
    fclose (finput);

    MDCT(amostras_saida, amostras_entrada, num_amostras, output);
    //DCT(amostras_saida, amostras_entrada, num_amostras, output);
}

//Transformada MDCT (formula: http://en.wikipedia.org/wiki/Modified_discrete_cosine_transform)
void MDCT (double *amostras_saida, unsigned short int *amostras_entrada, int num_amostras, char *output){
	int n, k, j, i;
	char sinais[(num_amostras/2)-1];
	FILE *foutput;
	float pi = 3.14159265358979323846;
	
	amostras_saida = (double *) malloc(sizeof(double) * ((num_amostras/2)-1));

	printf("Calculando o MDCT... Aguarde. \n");

	for(k=0; k<(num_amostras/2)-1; k++){
		for(n=0; n<(num_amostras); n++){
			amostras_saida[k] = ((double) amostras_entrada[n])*cos((pi/num_amostras)*(n+1/2+(num_amostras/2))*(k+1/2));
		}
	}
	
	//Salva o arquivo em binario
	/*foutput = fopen("SaidaTransf.txt", "w+");

	for(i=0; i<(num_amostras/2)-1; i++){
		fprintf(foutput, "%u", amostras_saida[i]);
	}

	for(k=0; k<(num_amostras/2)-1; k++){
		fprintf(foutput, "%u", sinais[k]);
	}*/

	foutput = fopen(output,"wb+");
	fwrite(amostras_saida,sizeof(int),((num_amostras/2)-1),foutput);
	//fwrite(sinais,sizeof(char),num_amostras,foutput);
	fclose(foutput);

	printf("O arquivo foi comprimido com sucesso. \n");

	//IMDCT(amostras_saida, foutput, num_amostras, output);
}

//Transformada inversa do MDCT (IMDCT)
void IMDCT (int *amostras_saida, FILE *foutput, int num_amostras, FILE *output){
	int n, k;
	FILE *fdescomprimido;
	unsigned short int *amostras_descomprimida;
	float pi = 3.14159265358979323846;

	amostras_descomprimida = (unsigned short int *) malloc(sizeof(unsigned short int) * num_amostras);

	printf("Calculando o IMDCT... Aguarde. \n");
    
    //Calculo da transformada inversa MDCT
	for(n=0; n<num_amostras; n++){
		for(k=0; k<(2*num_amostras-1); k++){
			amostras_descomprimida[n] = ((unsigned short int)amostras_saida[k])*cos((pi/num_amostras)*(n+1/2+(num_amostras/2))*(k+1/2));
		}
		printf("Calculando o IMDCT... Aguarde 2. \n");
		amostras_descomprimida[n] = amostras_descomprimida[n]/num_amostras;
	}

	printf("O arquivo foi descomprimido. \n");

	//fdescomprimido = fopen(output,"wb+");
	//fwrite(amostras_descomprimida,sizeof(unsigned short int),num_amostras,foutput);
	//fwrite(sinais,sizeof(char),num_amostras,foutput);
	//fclose(foutput);
}

//Transformada DCT
void DCT (double *amostras_saida, unsigned short int *amostras_entrada, int num_amostras, char *output){   	
    int n, k;
	FILE *foutput;
	
	int vetor_DCT[num_amostras][num_amostras];

	amostras_saida = (double *) malloc(sizeof(double) * (num_amostras));

	printf("Calculando o DCT... Aguarde. \n");
  
    //Calculo da transformada com DCT
    for(n=0; n<num_amostras; n++)
      vetor_DCT[0][n] = 1/sqrt(num_amostras) * ((double) amostras_entrada[n]);
      
    for(k=1; k<num_amostras; k++)
      for(n=0; n<num_amostras; n++){
          vetor_DCT[k][n] = sqrt(2.0/num_amostras) * cos(k*M_PI/(2.0*num_amostras)*(2.0*n+1)) * (double) amostras_entrada[n];
      }
      
    for(k=0; k<num_amostras; k++)
      amostras_saida[k]=0;
 
    for(k=0; k<num_amostras; k++)
      for(n=0; n<num_amostras; n++)
          amostras_saida[k]+=vetor_DCT[k][n];

	foutput = fopen(output,"wb+");
	fwrite(amostras_saida,sizeof(double),num_amostras,foutput);
	fclose(foutput);

	printf("O arquivo foi comprimido com sucesso. \n");
	
	//IDCT(amostras_saida, num_amostras);	
}

//Transformada inversa do DCT (IDCT)
void IDCT (double *amostras_entrada, int num_amostras){   	
    int n, k;
    char saida[32] = "Audio/\0";
	FILE *foutput2;
	double *amostras_saida;
 
	double vetor_IDCT[num_amostras][num_amostras];

	amostras_saida = (double *) malloc(sizeof(double)*num_amostras);

	printf("Calculando o IDCT... Aguarde. \n");
  
    //Calculo da transformada inversa do DCT
    for(n=0; n<num_amostras; n++)
      vetor_IDCT[0][n]= sqrt(1/num_amostras) * amostras_entrada[0] * cos(0);
      
    for(k=1; k<num_amostras; k++)
        for(n=0; n<num_amostras; n++)
        vetor_IDCT[k][n] = sqrt(2/num_amostras) * amostras_entrada[k] * cos(k*(2*n+1)*M_PI/(2*num_amostras));
      
    for(k=0; k<num_amostras; k++)
      amostras_saida[k]=0;
 
    for(k=0; k<num_amostras; k++)
      for(n=0; n<num_amostras; n++)
          amostras_saida[k]+=vetor_IDCT[n][k];

	foutput2 = fopen(saida,"wb+");
	fwrite(amostras_saida,sizeof(int),num_amostras,foutput2);
	fclose(foutput2);

	printf("O arquivo foi descomprimido com sucesso. \n");
}

void codificacao_transformada(char *input, char *output){
     int op;
     system("cls");
     printf("\n");
     printf("****************** (Des)Comprimir por Transformada ********************\n\n");
    
     do{
        printf("\n");
        printf("1 - Comprimir\n");
        printf("2 - Descomprimir\n");
        printf("3 - Voltar\n");
        scanf("%d", &op);
        printf("\n\n");
        
        switch (op)
        {
               case 1: comprimir_transformada(input, output);break;
               //case 2: descomprimir_transformada();break;
               default: break;
        }
        }while(op != 3);
}
